package com.sinoatma.physignsmonitor.utils

import android.util.Log

object PacketTool {
    // CRC-8 多项式
    private val CRC_POLY = 0x07u




    /**
     * 基于上一个数据包生成下一个数据包
     * @param lastPacket 上一个数据包（十六进制字符串，包含CRC）
     * @return 生成的下一个数据包，包含修改后的数据和CRC校验；如果失败则返回null
     */
    fun generateNextPacket(lastPacket: String): String? {
        // 先移除上一个数据包的CRC（最后两个字符）
        val dataWithoutCRC = if (lastPacket.length >= 2) lastPacket.substring(0, lastPacket.length - 2) else lastPacket

        val dataBytes = hexStringToByteArray(dataWithoutCRC)

        // 检查数据长度是否足够（至少需要包含要修改的位置）
        if (dataBytes.size < 4) {
            Log.e("zsy", "数据包长度不足，无法修改第4个字节")
            return null
        }

        // 获取第4个字节当前值（索引3，从0开始计数）
        val currentValue = (dataBytes[4].toInt() and 0xFF)
        // 计算下一个值（0-255循环）
        val nextValue = (currentValue + 1) % 256

        // 创建新字节数组副本
        val newDataBytes = dataBytes.copyOf()
        // 修改第4个字节为下一个值
        newDataBytes[4] = nextValue.toByte()
        // 生成带CRC校验的数据包
        return generatePacketWithCRC(newDataBytes)
    }


    /**
     * 变包函数：修改01前面一个字节的值，在0-255范围内循环生成新数据包
     * @param originalData 原始数据包（十六进制字符串）
     * @return 生成的新数据包列表，每个数据包包含修改后的数据和CRC校验
     */
    fun generatePackets(originalData: String): List<String> {
        val packets = mutableListOf<String>()
        val dataBytes = hexStringToByteArray(originalData)

        // 查找01字节的位置
        for (i in 0 until dataBytes.size - 1) {
            if (dataBytes[i + 1] == 0x01.toByte()) {
                // 找到01前面的字节位置i
                for (newValue in 0..255) {
                    // 创建新字节数组副本
                    val newDataBytes = dataBytes.copyOf()
                    // 修改i位置的字节为newValue
                    newDataBytes[i] = newValue.toByte()
                    // 生成带CRC校验的数据包
                    val packetWithCRC = generatePacketWithCRC(newDataBytes)
                    packets.add(packetWithCRC)
                }
                break // 只修改第一个找到的01前面的字节
            }
        }

        return packets
    }

    /**
     * 生成带CRC校验的数据包
     * @param dataBytes 原始数据字节数组（不包含CRC校验）
     * @return 带CRC校验的十六进制字符串数据包
     */
    fun generatePacketWithCRC(dataBytes: ByteArray): String {
        // 计算CRC校验值
        val crc = calculateCRC(dataBytes, dataBytes.size, 0u)
        // 拼接数据和CRC校验
        val dataWithCRC = dataBytes + crc.toByte()
        // 转换为十六进制字符串
        return byteArrayToHexString(dataWithCRC)
    }

    /**
     * 校验数据包的CRC是否正确
     * @param packet 带CRC校验的数据包（十六进制字符串）
     * @return CRC校验是否正确
     */
    fun verifyCRC(packet: String): Boolean {
        val dataBytes = hexStringToByteArray(packet)
        if (dataBytes.size < 2) {
            return false
        }

        // 提取数据部分（去掉最后一个字节的CRC校验）
        val dataPart = dataBytes.copyOf(dataBytes.size - 1)
        // 提取实际CRC校验值
        val actualCRC = dataBytes.last().toUInt() and 0xFFu

        // 计算期望的CRC校验值
        val expectedCRC = calculateCRC(dataPart, dataPart.size, 0u)

        // 比较实际CRC和期望CRC
        return actualCRC == expectedCRC
    }

    /**
     * 计算CRC-8校验值
     * @param dataBytes 数据字节数组
     * @param dataSize 数据长度
     * @param crcIn 初始CRC值
     * @return 计算得到的CRC-8校验值
     */
    private fun calculateCRC(dataBytes: ByteArray, dataSize: Int, crcIn: UInt): UInt {
        var crc = crcIn
        for (i in 0 until dataSize) {
            val dataTmp = dataBytes[i].toUInt() and 0xFFu
            crc = crc xor (dataTmp shl 0)

            for (cnt in 0 until 8) {
                if ((crc and 0x80u) != 0u) {
                    crc = (crc shl 1) xor CRC_POLY
                } else {
                    crc = crc shl 1
                }
            }
        }
        return crc and 0xFFu
    }

    /**
     * 十六进制字符串转字节数组
     */
    fun hexStringToByteArray(hex: String): ByteArray {
        return hex.chunked(2)
            .map { it.toInt(16).toByte() }
            .toByteArray()
    }

    /**
     * 字节数组转十六进制字符串
     */
    private fun byteArrayToHexString(bytes: ByteArray): String {
        return bytes.joinToString("") { String.format("%02X", it) }
    }
}